#pragma once
#include <mutex>
#include <list>
#include <thread>
#include <condition_variable>

#include "Log.hpp"
#include "room.hpp"
#include "Online.hpp"

template <class T>
class MatchQueue
{
public:
    void push(const T &data)
    {
        std::unique_lock<std::mutex> lock(_mtx);

        _list.push_back(data);
        
        // 入队数据就唤醒所有线程
        _cond.notify_all();
    }
    bool pop(T &data)
    {
        std::unique_lock<std::mutex> lock(_mtx);

        if (_list.empty())
            return false;
        data = _list.front();
        _list.pop_front();
        return true;
    }
    void remove(T &data)
    {
        std::unique_lock<std::mutex> lock(_mtx);

        _list.remove(data);

        // auto it = _list.begin();
        // while(it != _list.end())
        // {
        //     if(*it == data)
        //     {
        //         _list.erase(it);
        //         return true;
        //     }
        //     ++it;
        // }

        // return false;
    }

    int size()
    {
        std::unique_lock<std::mutex> lock(_mtx);
        return _list.size();
    }
    bool empty()
    {
        std::unique_lock<std::mutex> lock(_mtx);
        return _list.empty();
    }
    void wait()
    {
        std::unique_lock<std::mutex> lock(_mtx);
        _cond.wait(lock);
    }

private:
    std::list<T> _list;
    std::mutex _mtx;
    std::condition_variable _cond;
};

class Matcher
{
    typedef websocketpp::server<websocketpp::config::asio>::connection_ptr cp;

private:
    void handle_match(MatchQueue<int> &q)
    {
        while (true)
        {
            // 等待唤醒
            while (q.size() < 2)
            {
                q.wait();
            }

            // 取两个玩家
            int uid1, uid2;
            bool ret = q.pop(uid1);
            if (ret == false)
            {
                continue;
            }

            ret = q.pop(uid2);
            if (ret == false)
            {
                add(uid1);
                continue;
            }

            // 是否都在线
            cp conn1 = _oum->get_connect_from_hall(uid1);
            if (conn1.get() == nullptr)
            {
                add(uid2);
                continue;
            }
            cp conn2 = _oum->get_connect_from_hall(uid2);
            if (conn2.get() == nullptr)
            {
                add(uid1);
                continue;
            }

            // 创建房间
            room_ptr rp = _rm->create_room(uid1, uid2);
            if(rp.get() == nullptr)
            {
                add(uid1);
                add(uid2);
                continue;
            }

            // 响应
            Json::Value resp;
            resp["optype"] = "match_success";
            resp["result"] = true;
            std::string body;
            json_util::serialize(resp, body);
            conn1->send(body);
            conn2->send(body);
        }
    }

    void th_bronze_entry()
    {
        return handle_match(_q_bronze);
    }
    void th_silver_entry()
    {
        return handle_match(_q_silver);
    }
    void th_gold_entry()
    {
        return handle_match(_q_gold);
    }

public:
    Matcher(RoomManage *rm, UserCtl *uc, Online_User_Manage *oum)
        : _rm(rm), _uc(uc), _oum(oum),
          _th_bronze(std::thread(&Matcher::th_bronze_entry, this)),
          _th_silver(std::thread(&Matcher::th_silver_entry, this)),
          _th_gold(std::thread(&Matcher::th_gold_entry, this))
    {
        LOG(INFO, "游戏匹配模块初始化完毕\n");
    }

    bool add(int uid)
    {
        // 根据玩家的分数，添加到不同的匹配队列中

        Json::Value user;
        if (!_uc->SelectUserById(user, uid))
        {
            LOG(ERROR, "获取玩家信息失败\n");
            return false;
        }
        int score = user["score"].asInt();

        if (score < 2000)
            _q_bronze.push(uid);
        else if (score < 3000)
            _q_silver.push(uid);
        else
            _q_gold.push(uid);

        return true;
    }

    bool del(int uid)
    {
        Json::Value user;
        if (!_uc->SelectUserById(user, uid))
        {
            LOG(ERROR, "获取玩家信息失败\n");
            return false;
        }
        int score = user["score"].asInt();

        if (score < 2000)
            _q_bronze.remove(uid);
        else if (score < 3000)
            _q_silver.remove(uid);
        else
            _q_gold.remove(uid);

        return true;
    }

private:
    MatchQueue<int> _q_bronze;
    MatchQueue<int> _q_silver;
    MatchQueue<int> _q_gold;

    std::thread _th_bronze;
    std::thread _th_silver;
    std::thread _th_gold;
    RoomManage *_rm;
    UserCtl *_uc;
    Online_User_Manage *_oum;
};